import { Injectable } from '@nestjs/common';
import { formatDuration, formatFileSize } from './utils';
import * as fs from 'fs';
import * as NodeClam from 'clamscan';
import { PDFDocument, PDFName } from 'pdf-lib';

@Injectable()
export class AppService {
  private maxConcurrentRequests = 3; // 最大并发数
  private currentConcurrentRequests = 0; // 当前正在处理的请求数

  getHello(): string {
    return 'Hello World!';
  }

  getTestFile(fileType: string): string {
    let file = '';
    switch (fileType) {
      case 'html':
        file = 'asset/htmlfile.html';
        break;
      case 'jpg':
        file = 'asset/jpgfile.jpg';
        break;
      case 'js':
        file = 'asset/jsfile.js';
        break;
      case 'mp4':
        file = 'asset/mp4file.mp4';
        break;
      case 'pdf':
        // file = 'asset/pdffile.pdf';
        file = 'asset/xss-action.pdf';
        file = 'asset/xss-annotation-chrome.pdf';
        // file = 'asset/xss-openaction-firefox.pdf';
        break;
      case 'png':
        file = 'asset/pngfile.png';
        break;
      case 'svg':
        // file = 'asset/svgfile.svg';
        file = 'asset/xss.svg';
        break;
      case 'txt':
        file = 'asset/eicar.txt';
        break;
      default:
        file = 'pdf';
    }
    return file;
  }

  async scanFileWithClam(fileType: string): Promise<string> {
    if (this.currentConcurrentRequests >= this.maxConcurrentRequests) {
      // 如果处理请求的数量超过限制，返回 "服务忙" 错误
      console.error(`服务忙，请稍后再试, ${this.currentConcurrentRequests}`);
      return new Promise<string>((resolve, reject) => {
        resolve('服务忙，请稍后再试');
      });
    }
    this.currentConcurrentRequests++; // 增加并发计数
    console.log(`当前并发请求数: ${this.currentConcurrentRequests}`);

    const file = this.getTestFile(fileType);

    const options = {
      clamscan: {
        path: '/opt/homebrew/bin/clamscan', // 指定 clamscan 二进制文件路径
      },
      remove_infected: false, // 如果为 true，则自动删除受感染的文件
      quarantine_infected: false, // 如果为 true，则自动隔离受感染的文件
      scan_log: null, // 扫描日志文件路径
      debug_mode: false, // 是否启用调试模式
    };

    return new Promise<string>((resolve, reject) => {
      (async () => {
        const _now = new Date();
        try {
          // Explicitly type NodeClam and its instances
          type ClamScanType = {
            getVersion: () => Promise<string>;
            isInfected: (file: string) => Promise<{
              isInfected: boolean;
              file: string;
              viruses: string[];
            }>;
          };

          // NodeClam is a function that returns an object with an init method
          type NodeClamConstructor = new () => {
            init: (options: any) => Promise<ClamScanType>;
          };

          const nodeClam = NodeClam as unknown as NodeClamConstructor;
          if (typeof nodeClam !== 'function') {
            throw new Error('NodeClam is not a constructor');
          }
          const clamInstance = new nodeClam();
          if (typeof clamInstance.init !== 'function') {
            throw new Error('NodeClam instance does not have init method');
          }
          const clamscan = await clamInstance.init(options);
          if (
            !clamscan ||
            typeof clamscan.getVersion !== 'function' ||
            typeof clamscan.isInfected !== 'function'
          ) {
            throw new Error('ClamScan instance is invalid');
          }
          const version = await clamscan.getVersion();
          const fileSize = formatFileSize(fs.statSync(file).size);
          console.log(
            `ClamAV Version: ${version.replace(/\n/g, '')}, `,
            `Scanning file: ${file}, file size: ${fileSize}`,
          );

          const scanResult = await clamscan.isInfected(file);
          const { isInfected, file: theFile, viruses } = scanResult;
          const _end = new Date();
          const timeSpent = formatDuration(_end.getTime() - _now.getTime());
          console.log(
            `Scan completed in ${timeSpent}, file: ${theFile}, infected: ${isInfected}, viruses: ${viruses.join(', ')}`,
          );
          if (isInfected) {
            // console.log(`${theFile} is infected with ${viruses.join(', ')}!`);
            resolve(
              `${theFile} is infected with ${viruses.join(', ')}! Spent: ${timeSpent}`,
            );
          } else {
            resolve(
              `${theFile} is clean! Spent: ${timeSpent}, file size: ${fileSize}`,
            );
          }
        } catch (error) {
          resolve(`Error scanning file: ${error}`);
        } finally {
          this.currentConcurrentRequests--; // 请求处理完成后，减少计数
        }
      })().catch(reject);
    });
  }

  async scanPdf(file: string): Promise<string> {
    try {
      const fileData = fs.readFileSync(file);
      const pdfDoc = await PDFDocument.load(fileData);

      // 检查基本信息
      // const info = pdfDoc.getAuthor();
      // console.log('PDF Info:', info);

      // 检查所有页面内容
      const pages = pdfDoc.getPages();
      let suspiciousElementsFound = false;
      const findings: string[] = [];

      for (const page of pages) {
        // pdf-lib does not support direct text extraction; instead, check for suspicious entries in the page dictionary
        const jsPatterns = [
          PDFName.of('JS'),
          PDFName.of('JavaScript'),
          PDFName.of('AA'),
          PDFName.of('OpenAction'),
        ];
        const uriPatterns = [PDFName.of('URI')];

        const dict = page.node;
        // console.log('Page Dictionary:', dict, dict.has(PDFName.of('ArtBox')));

        // Check for JavaScript-related entries
        for (const jsPattern of jsPatterns) {
          if (dict.has(jsPattern)) {
            suspiciousElementsFound = true;
            findings.push('可疑的JavaScript代码');
            break;
          }
        }

        // Check for URI-related entries
        for (const uriPattern of uriPatterns) {
          if (dict.has(uriPattern)) {
            suspiciousElementsFound = true;
            findings.push('可疑的URI链接');
            break;
          }
        }

        if (dict.has(PDFName.of('AcroForm'))) {
          suspiciousElementsFound = true;
          findings.push('交互式表单字段，可能包含恶意脚本');
        }

        if (dict.has(PDFName.of('Launch'))) {
          suspiciousElementsFound = true;
          findings.push('启动外部应用程序的动作（罕见但危险）');
        }

        if (dict.has(PDFName.of('RichMedia'))) {
          suspiciousElementsFound = true;
          findings.push('富媒体内容（如 Flash），可能包含漏洞');
        }
      }

      // 检查文档级动作
      const catalog = pdfDoc.catalog;
      // console.log('Catalog:', catalog);
      // console.log('Catalog Entries:', catalog.get(PDFName.of('Metadata')));
      // console.log('Catalog Entries:', catalog.get(PDFName.of('Pages')));
      // console.log('Catalog Entries:', catalog.get(PDFName.of('Type')));
      if (
        catalog.get(PDFName.of('AA')) ||
        catalog.get(PDFName.of('OpenAction'))
      ) {
        suspiciousElementsFound = true;
        findings.push('文档级自动动作');
      }

      console.log({
        isSafe: !suspiciousElementsFound,
        findings: findings.length > 0 ? findings : '未发现可疑内容',
        info: {
          title: pdfDoc.getTitle(),
          author: pdfDoc.getAuthor(),
          subject: pdfDoc.getSubject(),
          creator: pdfDoc.getCreator(),
          producer: pdfDoc.getProducer(),
        },
      });
      return findings.length > 0 ? findings[0] : '未发现可疑内容';
    } catch (error) {
      return `Error scanning PDF: ${error}`;
    }

    // // 检查 PDF 的嵌入内容
    // console.log(111, pdfDoc.catalog.get(PDFName.of('Names')));
    // return 'abc';
    // const embeddedJavaScript = pdfDoc.catalog.get('Names')?.get('JavaScript');
    // if (embeddedJavaScript) {
    //   console.log('Embedded JavaScript detected in PDF!');
    //   console.log(embeddedJavaScript.toString());
    //   return 'risk detected: embedded JavaScript in PDF';
    // } else {
    //   console.log('No embedded JavaScript detected.');
    //   return 'pdf is clean';
    // }
  }

  async scanFileSpecific(fileType: string): Promise<string> {
    // 这里可以实现特定文件类型的扫描逻辑
    const file = this.getTestFile(fileType);
    switch (fileType) {
      case 'pdf':
        return await this.scanPdf(file);
      case 'html':
      case 'svg':
      case 'js':

      case 'jpg':
      case 'png':
      case 'mp4':
      case 'txt':
      default:
        return 'unsupported file type';
    }
    // return this.scanFile(fileType);
  }
}
